#pragma once

#define _hHuffmanDebug
namespace _hHuffmanDetail
{
	struct Node
	{
		size_t _freq = 0;
		Node* _left = NULL;
		Node* _right = NULL;

		virtual ~Node() {}
		virtual uint8_t getType() const { return 0; }
		virtual uint8_t getKey() const { return 0; }
		virtual uint16_t getCode() const { return 0; }
		virtual void fillBit(const hBitStr& inData) {}
		virtual void fillBitStr(hBitStr& outData) const { outData << 0; }
		virtual void fillOsStr(std::string& outData) {}
		virtual std::ostream& fillOs(std::ostream& os) const;
		size_t calcHeight() const;
		const Node* getNodeByHeight(size_t height, size_t idx) const;

	};

	struct CharNode : public Node
	{
		uint8_t _key = 0;

		uint8_t getKey() const { return _key; }
		uint16_t getCode() const { return 0x100 | _key; }
		std::ostream& fillOs(std::ostream& os) const;
	};

	struct StrBaseNode : public Node
	{
		std::map<uint8_t, hBitStr>* _pCharKeyTab;
		std::map<size_t, hBitStr>* _pLenKeyTab;

		void fillBitStr(hBitStr& outData) const;

	};

	struct StrNode : public StrBaseNode
	{
		hStrView _key;
		hBitStr _val;

		uint8_t getType() const { return 1; }
		//uint8_t getKey() const { return _key; }
		//uint16_t getCode() const { return 0x100 | _key; }
		void fillBit(const hBitStr& inData) { _val = inData; }
		void fillBitStr(hBitStr& outData) const;
		void fillOsStr(std::string& outData);
		std::ostream& fillOs(std::ostream& os) const;
	};

	struct LenNode : public Node
	{
		size_t _len;

		uint8_t getType() const { return 1; }
		uint8_t getKey() const { return (uint8_t)_len; }
		//uint16_t getCode() const { return 0x100 | _key; }
		void fillBitStr(hBitStr& outData) const;
		//void fillOsStr(std::string& outData);
		std::ostream& fillOs(std::ostream& os) const;
	};

	struct FillOsTreeCallback
	{
		enum NeedBitType
		{
			NeedBitType_Left = 0x01,
			NeedBitType_Right = 0x02,
		};

		Node* _pTree = NULL;
		size_t _nilNum = 2;
		size_t formatNum1 = 1;
		size_t formatNum2 = 2;
		size_t formatNum = 1;
		size_t _heightMax = 0;
		size_t _nodeMax = 0;
		size_t _curPos = 0;
		std::map<size_t, std::map<size_t, std::pair<size_t, size_t>>> _dataPos;
		std::map<size_t, std::map<size_t, std::string>> _dataStr;

		std::ostream& operator() (std::ostream& os);

	private:
		void init();
		void fillHeightMaxData(Node* pNode);
		void fillAllByHeightMaxData(const Node* pNode);
		std::ostream& fillOs(std::ostream& os) const;
		size_t fillTreeData(const Node* src, std::string& dst, size_t len = 0, size_t needFlag = 0);
		void freshFormatNum();
	};
}